﻿/*
    类 说 明：业务逻辑类，封装了数据表相关基本操作
	生成时间：2020/10/1 11:26:15
	生成工具：Kalman Studio
*/

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Threading.Tasks;

using Kalman;
using Newtonsoft.Json.Linq;
using System.Runtime.Serialization;
using Kalman.Extensions;
using TencentCloud.Ocr.V20181119.Models;
using Wechat.TenpayV3;
using Google.Protobuf.WellKnownTypes;
using K4os.Compression.LZ4.Internal;
using Wechat.MP;
using System.Reflection.Emit;
using System.Security.Cryptography;

namespace QuDao
{
    /// <summary>
    /// 数据表biz_order关联的业务对象，处理该表相关的业务逻辑
    /// </summary>
    public sealed partial class OrderBO : SingletonBase<OrderBO>
    {
        readonly IFreeSql fsql;
        readonly AppSettings appSettings;
        public OrderBO() : base()
        {
            fsql = ServiceLocator.Instance.GetRequiredService<IFreeSql>();
            appSettings = AppSettings.Load();
        }

        /// <summary>
        /// 获取订单
        /// </summary>
        /// <param name="id">主键ID的值</param>
        /// <returns></returns>
        public T GetItem<T>(long id)
        {
            var item = fsql.Select<Order>().Where(a => a.OrderID == id).ToOne<T>();
            return item;
        }

        public Order GetItem(long id) => GetItem<Order>(id);

        /// <summary>
        /// 删除订单数据
        /// </summary>
        /// <returns>返回影响行数</returns>
        public void Delete(long id)
        {
            var order = GetItem(id) ?? throw new AppException($"订单不存在：{id}");
            if (order.Status != (int)OrderStatus.已取消) throw new AppException("只能删除已取消状态的订单");

            fsql.Delete<Order>(id).ExecuteAffrows();
        }

        /// <summary>
        /// 后台查询数据（分页）
        /// </summary>
        /// <param name="req">分页查询请求对象</param>
        public PagingResult<T> Query<T>(PagingRequest req, int tid = 0, int status = 0)
        {
            var select = fsql.Select<Order>().WhereIf(tid > 0, a => a.TID == tid).WhereIf(status > 0, a => a.Status == status);

            foreach (var item in req.FilterItems)
            {
                switch (item.Field)
                {
                    case "OrderID":
                        select.Where(a => a.OrderID == long.Parse(item.Value));
                        break;
                    case "UserID":
                        select.Where(a => a.UserID == long.Parse(item.Value));
                        break;
                    case "TrueName":
                        select.Where(a => a.TrueName.StartsWith(item.Value));
                        break;
                    case "Mobile":
                        select.Where(a => a.Mobile.StartsWith(item.Value));
                        break;
                    default:
                        break;
                }
            }

            if (!string.IsNullOrEmpty(req.SortExp))
            {
                select.OrderBy(req.SortExp);//自定义排序
            }
            else
            {
                select.OrderByDescending(a => a.OrderID);//默认排序
            }

            var sql = select.ToSql();

            var list = select.Count(out var total).Page(req.PageIndex, req.PageSize).ToList<T>();
            return new PagingResult<T>
            {
                PageIndex = req.PageIndex,
                PageSize = req.PageSize,
                Data = list,
                Total = (int)total
            };
        }

        public PagingResult<Order> Query(PagingRequest req, int tid = 0, int status = 0)
        {
            var result = Query<Order>(req, tid, status);
            return result;
        }

        /// <summary>
        /// 获取订单详情
        /// </summary>
        public OrderDetailsDto GetDetails(long id)
        {
            var dto = GetItem<OrderDetailsDto>(id);
            dto.ItemList = OrderItemBO.Instance.GetList(id);
            return dto;
        }

        /// <summary>
        /// 获取订单列表
        /// </summary>
        /// <param name="tid">租户ID</param>
        /// <param name="pageIndex">页索引，从1开始</param>
        /// <param name="pageSize">每页记录数，默认10</param>
        /// <returns></returns>
        public IEnumerable<OrderDto> GetListByTID(int tid, int pageIndex = 1, int pageSize = 10)
        {
            var list = fsql.Select<Order>()
                .Where(a => a.TID == tid)
                .Page(pageIndex, pageSize)
                 .OrderByDescending(a => a.OrderID)
                 .ToList<OrderDto>();

            return list;
        }

        /// <summary>
        /// 获取订单列表
        /// </summary>
        /// <param name="tid">租户ID</param>
        /// <param name="uid">会员用户ID</param>
        /// <param name="status">订单状态：0-全部，10-待付款，11-已取消，20-待审核，30-待发货，100-已完成</param>
        /// <param name="pageIndex">页索引，从1开始</param>
        /// <param name="pageSize">每页记录数，默认10</param>
        /// <returns></returns>
        public IEnumerable<OrderDto> GetList(int tid, long uid, int status, int pageIndex = 1, int pageSize = 10)
        {
            var list = fsql.Select<Order>()
                .Where(a => a.TID == tid) 
                .WhereIf(uid > 0, a => a.UserID == uid)
                .WhereIf(status > 0, a => a.Status == status)
                .Page(pageIndex, pageSize)
                 .OrderByDescending(a => a.OrderID)
                 .ToList<OrderDto>();

            if (list.Count > 0)
            {
                var orderIDs = list.Select(p => p.OrderID).ToArray();
                var dic = OrderItemBO.Instance.GetDic(orderIDs);

                foreach (var item in list)
                {
                    if (dic.ContainsKey(item.OrderID))
                    {
                        item.ItemList = dic[item.OrderID];
                    }
                    else
                    {
                        item.ItemList = new List<OrderItemDto>();
                    }
                }
            }

            return list;
        }

        public static long GetNewID()
        {
            var id = long.Parse(DateTime.Now.ToString("yyyyMMddHHmmssff"));
            return id;
        }

        /// <summary>
        /// 获取会员用户折扣
        /// </summary>
        public decimal GetDiscount(User user, Tenant tenant)
        {
            return user.UserType switch
            {
                "A" => tenant.MADiscount,
                "B" => tenant.MBDiscount,
                _ => tenant.MCDiscount,
            };
        }

        /// <summary>
        /// 获取会员用户商品进货价
        /// </summary>
        public Tuple<decimal, decimal> GetMemberPriceAndCommssion(User user, Tenant tenant, Goods goods)
        {
            var memberPrice = decimal.Zero;
            var commission = decimal.Zero;
            if (goods.IsSpecial)
            {
                memberPrice = user.UserType switch
                {
                    "A" => goods.MAPrice,
                    "B" => goods.MBPrice,
                    _ => goods.MCPrice,
                };

                if (tenant.CommissionFlag != 0)
                {
                    commission = goods.Commission; //特价商品的业绩提成是固定的（商品信息中提前设置的）
                }
            }
            else
            {
                var discount = GetDiscount(user, tenant);
                memberPrice = decimal.Round(goods.MarketPrice * discount, 2);

                if (tenant.CommissionFlag != 0)
                {
                    commission = decimal.Round(tenant.CommissionRatio * memberPrice, 2);
                }
            }

            return new Tuple<decimal, decimal>(memberPrice, commission);
        }

        ///// <summary>
        ///// 获取订单业绩提成金额
        ///// </summary>
        //public decimal GetCommission(Tenant tenant, decimal orderAmount)
        //{
        //    if (tenant.CommissionFlag == 0) return decimal.Zero;
        //    return decimal.Round(tenant.CommissionRatio * orderAmount, 2);
        //}

        /// <summary>
        /// 创建订单
        /// </summary>
        /// <param name="tid">租户ID</param>
        /// <param name="uid">当前用户ID，传0可以获取所有用户订单</param>
        /// <param name="itemList">采购清单</param>
        /// <param name="deductionAmount">余额抵扣金额</param>
        /// <returns>返回新订单ID</returns>
        public long Create(int tid, long uid, List<OrderItemAddDto> itemList, decimal deductionAmount)
        {
            var tenant = TenantBO.Instance.GetItem(tid);
            var user = UserBO.Instance.GetItem(uid) ?? throw new AppException($"用户不存在或未登录：{uid}");
            if (user.Balance < deductionAmount) throw new AppException("订单抵扣金额不能大于用户账户余额");
            if (user.ReceiveAddress.IsNullOrEmpty() || user.ReceiverName.IsNullOrEmpty() || user.ReceiverPhone.IsNullOrEmpty()) throw new AppException("请先完善收货地址信息");

            var flag = fsql.Select<Order>().Where(a => a.UserID == uid && a.Status == 10).Any();
            if (flag) throw new AppException("您还有订单未付款，请先付款或取消该订单");

            var orderID = GetNewID();
            var order = new Order
            {
                TID = tid,
                OrderID = orderID,
                UserID = user.UserID,
                UserType = user.UserType,
                TrueName = user.TrueName,
                Mobile = user.Mobile,
                RecoID = user.RecoID,
                RecoName = user.RecoName,
                RecoID2 = user.RecoID2,
                RecoName2 = user.RecoName2,
                ReceiverPhone = user.ReceiverPhone,
                ReceiverName = user.ReceiverName,
                ReceivingAddress = user.ReceiveAddress,
                CountyID = user.CountyID,
                DeductionAmount = deductionAmount,
                Status = (int)OrderStatus.待付款,
                CreateTime = DateTime.Now,
            };

            var agent = UserBO.Instance.GetAgent(user, tenant);
            if(agent != null)
            {
                order.AgentID = agent.UserID;
                order.AgentName = agent.TrueName;
            }

            var orderItemList = new List<OrderItem>();
            foreach (var item in itemList)
            {
                var goods = GoodsBO.Instance.GetItem(item.GoodsID);
                if (goods == null || goods.Status != 1) throw new AppException($"商品不存在或已下架：{item.GoodsID}");
                if (item.Num <= 0) throw new AppException($"下单数量无效：{item.Num}");

                var orderItem = new OrderItem
                {
                    OrderItemID = OrderItemBO.GetNewID(),
                    TID = tid,
                    UserID = user.UserID,
                    OrderID = orderID,
                    GoodsID = item.GoodsID,
                    GoodsName = goods.GoodsName,
                    ImageUrl = goods.ImageUrl,
                    CategoryID = goods.CategoryID,
                    CategoryName = goods.CategoryName,
                    Num = item.Num,
                    MarketPrice = goods.MarketPrice,
                    CostPrice = goods.CostPrice,
                    //Price = GetMemberPriceAndCommssion(user, tenant, goods), 
                    CreateTime = DateTime.Now, 
                };

                var tuple = GetMemberPriceAndCommssion(user,tenant, goods);
                orderItem.Price = tuple.Item1;
                orderItem.Commission = tuple.Item2; //单个商品的业绩提成

                order.Commission += tuple.Item2 * orderItem.Num;
                order.Amount += tuple.Item1 * orderItem.Num;
                order.Cost += orderItem.CostPrice * orderItem.Num;
                order.Total += orderItem.Num;

                orderItemList.Add(orderItem);
            }

            //order.Commission = GetCommission(tenant, order.Amount);
            var profit = order.Amount - order.Cost - order.Commission;
            order.L1Fee = decimal.Round(profit * tenant.L1Ratio, 2);
            order.L2Fee = profit - order.L1Fee;

            if (deductionAmount > order.Amount) throw new AppException("订单抵扣金额不能大于订单总金额");
            if (deductionAmount == order.Amount) order.Status = (int)OrderStatus.待发货; //全款抵扣不用财务审核了

            fsql.Transaction(() =>
            {
                if(deductionAmount > decimal.Zero) fsql.Update<User>(uid).Set(a => a.Balance - deductionAmount).ExecuteAffrows(); //先扣除余额抵扣金额，取消订单时要返还
                fsql.Insert(order).ExecuteAffrows();
                fsql.Insert(orderItemList).ExecuteAffrows();
            });

            return orderID;
        }

        /// <summary>
        /// 取消订单
        /// </summary>
        /// <param name="orderID"></param>
        /// <returns></returns>
        public void Cancel(long orderID)
        {
            var order = GetItem(orderID) ?? throw new AppException($"订单不存在：{orderID}");
            if (order.Status != (int)OrderStatus.待付款) throw new AppException("只能取消待付款状态的订单");

            fsql.Transaction(() =>
            {
                if (order.DeductionAmount > decimal.Zero)
                {
                    fsql.Update<User>(order.UserID).Set(a => a.Balance + order.DeductionAmount).ExecuteAffrows(); //取消订单返还抵扣余额
                }
                //fsql.Update<Order>(orderID).Set(a => a.Status, (int)OrderStatus.已取消).ExecuteAffrows();
                fsql.Delete<Order>(orderID).ExecuteAffrows(); //取消直接删除
            });
        }

        /// <summary>
        /// 确认付款
        /// </summary>
        /// <param name="orderID">订单ID</param>
        /// <param name="payeeAccount">转到哪个账号</param>
        /// <param name="transferDate">转账日期</param>
        /// <param name="tradeNo">交易流水号（可空）</param>
        /// <param name="memo">转账备注（可空）</param>
        /// <param name="transferImage">打款凭证图片</param>
        /// <returns></returns>
        public void ConfirmPayment(long orderID, string payeeAccount, DateTime transferDate, string tradeNo = "", string memo = "", string transferImage = "")
        {
            var order = GetDetails(orderID) ?? throw new AppException($"订单不存在：{orderID}");
            if (order.Status != (int)OrderStatus.待付款) throw new AppException("订单非待付款状态");
            if (payeeAccount.IsNullOrEmpty()) throw new AppException("收款账号不能为空");
            if (transferImage.IsNullOrEmpty() || transferImage == "undefined" || transferImage.Length < 20) throw new AppException("打款凭证图片不能为空");

            fsql.Transaction(() =>
            {
                fsql.Update<Order>(order.OrderID)
                .Set(a => a.Status, (int)OrderStatus.待审核)
                .Set(a => a.PayeeAccount, payeeAccount)
                .Set(a => a.TransferDate, transferDate)
                .Set(a => a.TradeNo, tradeNo)
                .Set(a => a.TransferImage, transferImage)
                .Set(a => a.TransferMemo, memo)
                .ExecuteAffrows();

                if (order.DeductionAmount > decimal.Zero)
                {
                    var cashApply = new CashApply
                    {
                        TID = order.TID,
                        UserID = order.UserID,
                        TrueName = order.TrueName,
                        Amount = order.DeductionAmount,
                        Target = 2, //余额抵扣货款
                        Status = 1,
                        OperatorID = 0,
                        OperatorName = "系统",
                        TradeNo = order.OrderID.ToString(),
                        PaymentTime = DateTime.Now,
                        CreateTime = DateTime.Now,
                        Memo = $"订单号：{order.OrderID}"
                    };
                    fsql.Insert(cashApply).ExecuteIdentity();
                }

                foreach (var item in order.ItemList)
                {
                    fsql.Update<Goods>(item.GoodsID).Set(a => a.SalesNum + item.Num).ExecuteAffrows();
                }
            });
        }

        /// <summary>
        /// 付款审核
        /// </summary>
        /// <param name="auditorID">审核员用户ID</param>
        /// <param name="orderID">订单ID</param>
        /// <param name="payeeAccount">转到哪个账号（审核时可以修改）</param>
        /// <param name="transferDate">转账日期（审核时可以修改）</param>
        /// <param name="tradeNo">交易流水号（审核时可以修改）</param>
        /// <returns></returns>
        public void PaymentAuditing(long auditorID, long orderID, string payeeAccount, DateTime? transferDate, string tradeNo)
        {
            var auditor = UserBO.Instance.GetItem(auditorID) ?? throw new AppException($"财务审核员用户不存在：{auditorID}");
            if (auditor.Role != 1 && auditor.Role != 9) throw new AppException($"当前用户没有审核权限：{auditorID}");
            if (payeeAccount.IsNullOrEmpty()) throw new AppException("收款账号不能为空");

            var order = GetItem(orderID) ?? throw new AppException($"订单不存在：{orderID}");
            if (order.Status != (int)OrderStatus.待审核) throw new AppException("订单非待审核状态");

            fsql.Update<Order>(order.OrderID)
                .Set(a => a.Status, (int)OrderStatus.待发货)
                //.SetIf(order.PayeeAccount != payeeAccount && payeeAccount.IsNullOrEmpty(), a => a.PayeeAccount, payeeAccount)
                .SetIf(!payeeAccount.IsNullOrEmpty(), a => a.PayeeAccount, payeeAccount)
                .SetIf(transferDate != null, a => a.TransferDate, transferDate)
                .SetIf(!tradeNo.IsNullOrEmpty(), a => a.TradeNo, tradeNo)
                .Set(a => a.AuditorID, auditorID)
                .Set(a => a.AuditorName, auditor.TrueName)
                .Set(a => a.AuditingTime, DateTime.Now)
                .ExecuteAffrows();
        }

        /// <summary>
        /// 退回（撤回）订单（会员由于某种原因不能及时打款，可以退回到待付款状态）
        /// </summary>
        public void Return(long orderID)
        {
            var order = GetItem(orderID) ?? throw new AppException($"订单不存在：{orderID}");
            if (order.Status != (int)OrderStatus.待审核) throw new AppException("订单非待审核状态");

            fsql.Update<Order>(orderID).Set(a => a.Status, (int)OrderStatus.待付款).ExecuteAffrows();
        }

        /// <summary>
        /// 确认发货
        /// </summary>
        /// <param name="shipperID">发货人用户ID</param>
        /// <param name="orderID">订单ID</param>
        /// <param name="expressCompany">快递公司名称</param>
        /// <param name="expressNo">快递单号</param>
        /// <returns></returns>
        public void ConfirmDelivery(long shipperID, long orderID, string expressCompany, string expressNo)
        {
            var shipper = UserBO.Instance.GetItem(shipperID) ?? throw new AppException($"发货人用户不存在：{shipperID}");
            if (shipper.Role != 2 && shipper.Role != 9) throw new AppException($"当前用户没有发货确认权限：{shipperID}");
            if (expressCompany.IsNullOrEmpty()) throw new AppException("快递公司不能为空");
            if (expressNo.IsNullOrEmpty()) throw new AppException("快递单号不能为空");

            var order = GetItem(orderID) ?? throw new AppException($"订单不存在：{orderID}");
            if (order.Status != (int)OrderStatus.待发货) throw new AppException("订单非待发货状态");

            var billList = new List<Bill>();
            if(order.RecoID > 0 && order.L1Fee > decimal.Zero)
            {
                billList.Add(new Bill
                {
                    BillType = 1, //业务奖励，直推奖励
                    TID = order.TID,
                    UserID = order.RecoID,
                    TrueName = order.RecoName,
                    AreaID = order.CountyID,
                    RelationUserID = order.UserID,
                    RelationUserName = order.TrueName,
                    RelationOrderID = order.OrderID,
                    RelationOrderAmount = order.Amount,
                    Amount = order.L1Fee,
                    ClearingDate = DateTime.Now.Date,
                    CreateTime = DateTime.Now,
                    Memo = "业务奖励"
                });
            }
            if (order.RecoID2 > 0 && order.L2Fee > decimal.Zero)
            {
                billList.Add(new Bill
                {
                    BillType = 2, //团队奖励，间推奖励
                    TID = order.TID,
                    UserID = order.RecoID2,
                    TrueName = order.RecoName2,
                    AreaID = order.CountyID,
                    RelationUserID = order.UserID,
                    RelationUserName = order.TrueName,
                    RelationOrderID = order.OrderID,
                    RelationOrderAmount = order.Amount,
                    Amount = order.L2Fee,
                    ClearingDate = DateTime.Now.Date,
                    CreateTime = DateTime.Now,
                    Memo = "团队奖励"
                });
            }
            if (order.AgentID > 0 && order.Commission > decimal.Zero)
            {
                billList.Add(new Bill
                {
                    BillType = 9, //业绩提成
                    TID = order.TID,
                    UserID = order.AgentID,
                    TrueName = order.AgentName,
                    AreaID = order.CountyID,
                    RelationUserID = order.UserID,
                    RelationUserName = order.TrueName,
                    RelationOrderID = order.OrderID,
                    RelationOrderAmount = order.Amount,
                    Amount = order.Commission,
                    ClearingDate = DateTime.Now.Date,
                    CreateTime = DateTime.Now,
                    Memo = "业绩提成"
                });
            }

            var salesDaily = SalesDailyBO.Instance.GetItem(order.AgentID, order.CreateTime.Date);
            fsql.Transaction(() =>
            {
                if (salesDaily != null)
                {
                    fsql.Update<SalesDaily>(salesDaily.ID)
                        .Set(a => a.OrderNum + 1)
                        .Set(a => a.Amount + order.Amount)
                        .Set(a => a.DividedAmount + (order.Amount - order.Cost))
                        .Set(a => a.DeductionAmount + order.DeductionAmount)
                        .ExecuteAffrows();
                }
                else
                {
                    salesDaily = new SalesDaily
                    {
                        TID = order.TID,
                        AgentID = order.AgentID,
                        AgentName = order.AgentName,
                        SalesDate = order.CreateTime.Date,
                        AreaID = order.CountyID,
                        OrderNum = 1,
                        Amount = order.Amount,
                        DividedAmount = order.Amount - order.Cost,
                        DeductionAmount = order.DeductionAmount,
                        CreateTime = DateTime.Now
                    };

                    fsql.Insert(salesDaily).ExecuteIdentity();
                }

                if (billList.Count > 0)
                {
                    fsql.Insert(billList).ExecuteAffrows();

                    foreach (var bill in billList)
                    {
                        fsql.Update<User>(bill.UserID)
                            .Set(a => a.Balance + bill.Amount)
                            .Set(a => a.TotalBonus + bill.Amount)
                            .ExecuteAffrows();
                    }
                }

                fsql.Update<Order>(orderID)
                .Set(a => a.Status, (int)OrderStatus.已完成)
                .Set(a => a.ExpressCompany, expressCompany)
                .Set(a => a.ExpressNo, expressNo)
                .Set(a => a.ShipperID, shipperID)
                .Set(a => a.ShipperName, shipper.TrueName)
                .Set(a => a.DeliveryTime, DateTime.Now)
                .ExecuteAffrows();
            });
            
        }

        //public List<OrderDto> GetSummaryByUser(long uid, DateTime beginDate, DateTime endDate, int pageIndex = 1, int pageSize = 10)
        //{
        //    var list = fsql.Select<Order>()
        //        .Where(a => a.UserID == uid && a.CreateTime >= beginDate && a.CreateTime <= endDate)

        //}
    }
}
